HashSet 简介 HashSet 是一个没有重复元素的集合。
它是由HashMap实现的,不保证元素的顺序,而且HashSet允许使用 null 元素。 HashSet是非同步的。如果多个线程同时访问一个哈希 set,而其中至少一个线程修改了该 set,那么它必须 保持外部同步。这通常是通过对自然封装该 set 的对象执行同步操作来完成的。如果不存在这样的对象,则应该使用 Collections.synchronizedSet 方法来“包装” set。最好在创建时完成这一操作,以防止对该 set 进行意外的不同步访问:
1 Set s = Collections.synchronizedSet(new HashSet(...));
HashSet通过iterator()返回的迭代器是fail-fast的。
HashSet的构造函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public HashSet () public HashSet (Collection<? extends E> c) public HashSet (int initialCapacity, float loadFactor) public HashSet (int initialCapacity) HashSet (int initialCapacity, float loadFactor, boolean dummy)
HashSet的主要API 1 2 3 4 5 6 7 8 boolean add (E object) void clear () Object clone () boolean contains (Object object) boolean isEmpty () Iterator<E> iterator () boolean remove (Object object) int size ()
HashSet数据结构 HashSet的继承关系如下:1 2 3 4 5 6 7 8 java.lang.Object ↳ java.util.AbstractCollection<E> ↳ java.util.AbstractSet<E> ↳ java.util.HashSet<E> public class HashSet <E > extends AbstractSet <E > implements Set <E >, Cloneable , java .io .Serializable { }
HashSet与Map关系图:
从图中可以看出:
HashSet继承于AbstractSet,并且实现了Set接口。
HashSet的本质是一个”没有重复元素”的集合,它是通过HashMap实现的。HashSet中含有一个”HashMap类型的成员变量”map,HashSet的操作函数,实际上都是通过map实现的。
HashSet源码解析(基于JDK1.6.0_45) 为了更了解HashSet的原理,下面对HashSet源码代码作出分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 package java.util;public class HashSet <E > extends AbstractSet <E > implements Set <E >, Cloneable , java .io .Serializable { static final long serialVersionUID = -5024744406713321676L ; private transient HashMap<E,Object> map; private static final Object PRESENT = new Object(); public HashSet () { map = new HashMap<E,Object>(); } public HashSet (Collection<? extends E> c) { map = new HashMap<E,Object>(Math.max((int ) (c.size()/.75f ) + 1 , 16 )); addAll(c); } public HashSet (int initialCapacity, float loadFactor) { map = new HashMap<E,Object>(initialCapacity, loadFactor); } public HashSet (int initialCapacity) { map = new HashMap<E,Object>(initialCapacity); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor); } public Iterator<E> iterator () { return map.keySet().iterator(); } public int size () { return map.size(); } public boolean isEmpty () { return map.isEmpty(); } public boolean contains (Object o) { return map.containsKey(o); } public boolean add (E e) { return map.put(e, PRESENT)==null ; } public boolean remove (Object o) { return map.remove(o)==PRESENT; } public void clear () { map.clear(); } public Object clone () { try { HashSet<E> newSet = (HashSet<E>) super .clone(); newSet.map = (HashMap<E, Object>) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(map.capacity()); s.writeFloat(map.loadFactor()); s.writeInt(map.size()); for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } private void readObject (java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); int capacity = s.readInt(); float loadFactor = s.readFloat(); map = (((HashSet)this ) instanceof LinkedHashSet ? new LinkedHashMap<E,Object>(capacity, loadFactor) : new HashMap<E,Object>(capacity, loadFactor)); int size = s.readInt(); for (int i=0 ; i<size; i++) { E e = (E) s.readObject(); map.put(e, PRESENT); } } }
说明: HashSet的代码实际上非常简单,通过上面的注释应该很能够看懂。它是通过HashMap实现的,若对HashSet的理解有困难,建议先学习以下HashMap;学完HashMap之后,在学习HashSet就非常容易了。
HashSet遍历方式 通过Iterator遍历HashSet 第一步:根据iterator()获取HashSet的迭代器。 第二步:遍历迭代器获取各个元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 for (Iterator iterator = set.iterator(); iterator.hasNext(); ) { iterator.next(); } ``` ## 通过for-each遍历HashSet 第一步:根据toArray()获取HashSet的元素集合对应的数组。 第二步:遍历数组,获取各个元素。 String[] arr = (String[])set.toArray(new String[0 ]); for (String str:arr) System.out.printf("for each : %s\n" , str); HashSet的遍历测试程序如下: ```java import java.util.Random;import java.util.Iterator;import java.util.HashSet;public class HashSetIteratorTest { public static void main (String[] args) { HashSet set = new HashSet(); for (int i=0 ; i<5 ; i++) set.add("" +i); iteratorHashSet(set) ; foreachHashSet(set); } private static void iteratorHashSet (HashSet set) { for (Iterator iterator = set.iterator(); iterator.hasNext(); ) { System.out.printf("iterator : %s\n" , iterator.next()); } } private static void foreachHashSet (HashSet set) { String[] arr = (String[])set.toArray(new String[0 ]); for (String str:arr) System.out.printf("for each : %s\n" , str); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 iterator : 3 iterator : 2 iterator : 1 iterator : 0 iterator : 4 for each : 3 for each : 2 for each : 1 for each : 0 for each : 4
HashSet示例 下面我们通过实例学习如何使用HashSet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.util.Iterator;import java.util.HashSet;public class HashSetTest { public static void main (String[] args) { testHashSetAPIs() ; } private static void testHashSetAPIs () { HashSet set = new HashSet(); set.add("a" ); set.add("b" ); set.add("c" ); set.add("d" ); set.add("e" ); System.out.printf("size : %d\n" , set.size()); System.out.printf("HashSet contains a :%s\n" , set.contains("a" )); System.out.printf("HashSet contains g :%s\n" , set.contains("g" )); set.remove("e" ); String[] arr = (String[])set.toArray(new String[0 ]); for (String str:arr) System.out.printf("for each : %s\n" , str); HashSet otherset = new HashSet(); otherset.add("b" ); otherset.add("c" ); otherset.add("f" ); HashSet removeset = (HashSet)set.clone(); removeset.removeAll(otherset); System.out.printf("removeset : %s\n" , removeset); HashSet retainset = (HashSet)set.clone(); retainset.retainAll(otherset); System.out.printf("retainset : %s\n" , retainset); for (Iterator iterator = set.iterator(); iterator.hasNext(); ) System.out.printf("iterator : %s\n" , iterator.next()); set.clear(); System.out.printf("%s\n" , set.isEmpty()?"set is empty" :"set is not empty" ); } }
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 size : 5 HashSet contains a :true HashSet contains g :false for each : dfor each : bfor each : cfor each : aremoveset : [d, a] retainset : [b, c] iterator : d iterator : b iterator : c iterator : a set is empty